/*
 * Copyright (c) 2007-2012, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
/*
 * Copyright 1999-2004 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 * $Id: DTMDocumentImpl.java,v 1.2.4.1 2005/09/15 08:15:01 suresh_emailid Exp $
 */
package com.sun.org.apache.xml.internal.dtm.ref;

import javax.xml.transform.SourceLocator;

import com.sun.org.apache.xml.internal.dtm.DTM;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.dtm.DTMAxisTraverser;
import com.sun.org.apache.xml.internal.dtm.DTMManager;
import com.sun.org.apache.xml.internal.dtm.DTMWSFilter;
import com.sun.org.apache.xml.internal.utils.FastStringBuffer;
import com.sun.org.apache.xml.internal.utils.XMLString;
import com.sun.org.apache.xml.internal.utils.XMLStringFactory;

import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.Locator;
import org.xml.sax.ext.LexicalHandler;

/**
 * This is the implementation of the DTM document interface.  It receives
 * requests from an XML content handler similar to that of an XML DOM or SAX parser
 * to store information from the xml document in an array based
 * dtm table structure.  This informtion is used later for document navigation,
 * query, and SAX event dispatch functions. The DTM can also be used directly as a
 * document composition model for an application.  The requests received are:
 * <ul>
 * <li>initiating DTM to set the doc handle</li>
 * <li>resetting DTM for data structure reuse</li>
 * <li>hinting the end of document to adjust the end of data structure pointers</li>
 * <li>createnodes (element, comment, text, attribute, ....)</li>
 * <li>hinting the end of an element to patch parent and siblings<li>
 * <li>setting application provided symbol name stringpool data structures</li>
 * </ul>
 * <p>State: In progress!!</p>
 *
 * %REVIEW% I _think_ the SAX convention is that "no namespace" is expressed
 * as "" rather than as null (which is the DOM's convention). What should
 * DTM expect? What should it do with the other?
 *
 * <p>Origin: the implemention is a composite logic based on the DTM of XalanJ1 and
 *     DocImpl, DocumentImpl, ElementImpl, TextImpl, etc. of XalanJ2</p>
 */
public class DTMDocumentImpl implements DTM, org.xml.sax.ContentHandler, org.xml.sax.ext.LexicalHandler {

    // Number of lower bits used to represent node index.
    protected static final byte DOCHANDLE_SHIFT = 22;
    // Masks the lower order of node handle.
    // Same as {@link DTMConstructor.IDENT_NODE_DEFAULT}
    protected static final int NODEHANDLE_MASK = (1 << (DOCHANDLE_SHIFT + 1)) - 1;
    // Masks the higher order Document handle
    // Same as {@link DTMConstructor.IDENT_DOC_DEFAULT}
    protected static final int DOCHANDLE_MASK = -1 - NODEHANDLE_MASK;

    int m_docHandle = NULL; // masked document handle for this dtm document
    int m_docElement = NULL; // nodeHandle to the root of the actual dtm doc content

    // Context for parse-and-append operations
    int currentParent = 0; // current parent - default is document root
    int previousSibling = 0; // previous sibling - no previous sibling
    protected int m_currentNode = -1; // current node

    // The tree under construction can itself be used as
    // the element stack, so m_elemStack isn't needed.
    //protected Stack m_elemStack = new Stack();     // element stack

    private boolean previousSiblingWasParent = false;
    // Local cache for record-at-a-time fetch
    int gotslot[] = new int[4];

    // endDocument recieved?
    private boolean done = false;
    boolean m_isError = false;

    private final boolean DEBUG = false;

    /** The document base URI. */
    protected String m_documentBaseURI;

    /** If we're building the model incrementally on demand, we need to
     * be able to tell the source when to send us more data.
     *
     * Note that if this has not been set, and you attempt to read ahead
     * of the current build point, we'll probably throw a null-pointer
     * exception. We could try to wait-and-retry instead, as a very poor
     * fallback, but that has all the known problems with multithreading
     * on multiprocessors and we Don't Want to Go There.
     *
     * @see setIncrementalSAXSource
     */
    private IncrementalSAXSource m_incrSAXSource = null;

    // ========= DTM data structure declarations. ==============

    // nodes array: integer array blocks to hold the first level reference of the nodes,
    // each reference slot is addressed by a nodeHandle index value.
    // Assumes indices are not larger than {@link NODEHANDLE_MASK}
    // ({@link DOCHANDLE_SHIFT} bits).
    ChunkedIntArray nodes = new ChunkedIntArray(4);

    // text/comment table: string buffer to hold the text string values of the document,
    // each of which is addressed by the absolute offset and length in the buffer
    private FastStringBuffer m_char = new FastStringBuffer();
    // Start of string currently being accumulated into m_char;
    // needed because the string may be appended in several chunks.
    private int m_char_current_start = 0;

    // %TBD% INITIALIZATION/STARTUP ISSUES
    // -- Should we really be creating these, or should they be
    // passed in from outside? Scott want to be able to share
    // pools across multiple documents, so setting them here is
    // probably not the right default.
    private DTMStringPool m_localNames = new DTMStringPool();
    private DTMStringPool m_nsNames = new DTMStringPool();
    private DTMStringPool m_prefixNames = new DTMStringPool();

    // %TBD% If we use the current ExpandedNameTable mapper, it
    // needs to be bound to the NS and local name pools. Which
    // means it needs to attach to them AFTER we've resolved their
    // startup. Or it needs to attach to this document and
    // retrieve them each time. Or this needs to be
    // an interface _implemented_ by this class... which might be simplest!
    private ExpandedNameTable m_expandedNames = new ExpandedNameTable();

    private XMLStringFactory m_xsf;

    /**
     * Construct a DTM.
     *
     * @param documentNumber the ID number assigned to this document.
     * It will be shifted up into the high bits and returned as part of
     * all node ID numbers, so those IDs indicate which document they
     * came from as well as a location within the document. It is the
     * DTMManager's responsibility to assign a unique number to each
     * document.
     */
    public DTMDocumentImpl(DTMManager mgr, int documentNumber, DTMWSFilter whiteSpaceFilter, XMLStringFactory xstringfactory) {
        initDocument(documentNumber); // clear nodes and document handle
        m_xsf = xstringfactory;
    }

    /** Bind a IncrementalSAXSource to this DTM. If we discover we need nodes
     * that have not yet been built, we will ask this object to send us more
     * events, and it will manage interactions with its data sources.
     *
     * Note that we do not actually build the IncrementalSAXSource, since we don't
     * know what source it's reading from, what thread that source will run in,
     * or when it will run.
     *
     * @param source The IncrementalSAXSource that we want to recieve events from
     * on demand.
     */
    public void setIncrementalSAXSource(IncrementalSAXSource source) {
        m_incrSAXSource = source;

        // Establish SAX-stream link so we can receive the requested data
        source.setContentHandler(this);
        source.setLexicalHandler(this);

        // Are the following really needed? IncrementalSAXSource doesn't yet
        // support them, and they're mostly no-ops here...
        //source.setErrorHandler(this);
        //source.setDTDHandler(this);
        //source.setDeclHandler(this);
    }

    /**
     * Wrapper for ChunkedIntArray.append, to automatically update the
     * previous sibling's "next" reference (if necessary) and periodically
     * wake a reader who may have encountered incomplete data and entered
     * a wait state.
     * @param w0 int As in ChunkedIntArray.append
     * @param w1 int As in ChunkedIntArray.append
     * @param w2 int As in ChunkedIntArray.append
     * @param w3 int As in ChunkedIntArray.append
     * @return int As in ChunkedIntArray.append
     * @see ChunkedIntArray.append
     */
    private final int appendNode(int w0, int w1, int w2, int w3) {
        // A decent compiler may inline this.
        int slotnumber = nodes.appendSlot(w0, w1, w2, w3);

        if (DEBUG)
            System.out.println(slotnumber + ": " + w0 + " " + w1 + " " + w2 + " " + w3);

        if (previousSiblingWasParent)
            nodes.writeEntry(previousSibling, 2, slotnumber);

        previousSiblingWasParent = false; // Set the default; endElement overrides

        return slotnumber;
    }

    // ========= DTM Implementation Control Functions. ==============

    /**
     * Set an implementation dependent feature.
     * <p>
     * %REVIEW% Do we really expect to set features on DTMs?
     *
     * @param featureId A feature URL.
     * @param state true if this feature should be on, false otherwise.
     */
    public void setFeature(String featureId, boolean state) {
    };

    /**
     * Set a reference pointer to the element name symbol table.
     * %REVIEW% Should this really be Public? Changing it while
     * DTM is in use would be a disaster.
     *
     * @param poolRef DTMStringPool reference to an instance of table.
     */
    public void setLocalNameTable(DTMStringPool poolRef) {
        m_localNames = poolRef;
    }

    /**
     * Get a reference pointer to the element name symbol table.
     *
     * @return DTMStringPool reference to an instance of table.
     */
    public DTMStringPool getLocalNameTable() {
        return m_localNames;
    }

    /**
     * Set a reference pointer to the namespace URI symbol table.
     * %REVIEW% Should this really be Public? Changing it while
     * DTM is in use would be a disaster.
     *
     * @param poolRef DTMStringPool reference to an instance of table.
     */
    public void setNsNameTable(DTMStringPool poolRef) {
        m_nsNames = poolRef;
    }

    /**
     * Get a reference pointer to the namespace URI symbol table.
     *
     * @return DTMStringPool reference to an instance of table.
     */
    public DTMStringPool getNsNameTable() {
        return m_nsNames;
    }

    /**
     * Set a reference pointer to the prefix name symbol table.
     * %REVIEW% Should this really be Public? Changing it while
     * DTM is in use would be a disaster.
     *
     * @param poolRef DTMStringPool reference to an instance of table.
     */
    public void setPrefixNameTable(DTMStringPool poolRef) {
        m_prefixNames = poolRef;
    }

    /**
     * Get a reference pointer to the prefix name symbol table.
     *
     * @return DTMStringPool reference to an instance of table.
     */
    public DTMStringPool getPrefixNameTable() {
        return m_prefixNames;
    }

    /**
     * Set a reference pointer to the content-text repository
     *
     * @param buffer FastStringBuffer reference to an instance of
     * buffer
     */
    void setContentBuffer(FastStringBuffer buffer) {
        m_char = buffer;
    }

    /**
     * Get a reference pointer to the content-text repository
     *
     * @return FastStringBuffer reference to an instance of buffer
     */
    FastStringBuffer getContentBuffer() {
        return m_char;
    }

    /** getContentHandler returns "our SAX builder" -- the thing that
     * someone else should send SAX events to in order to extend this
     * DTM model.
     *
     * @return null if this model doesn't respond to SAX events,
     * "this" if the DTM object has a built-in SAX ContentHandler,
     * the IncrementalSAXSource if we're bound to one and should receive
     * the SAX stream via it for incremental build purposes...
     * */
    public org.xml.sax.ContentHandler getContentHandler() {
        if (m_incrSAXSource instanceof IncrementalSAXSource_Filter)
            return (ContentHandler) m_incrSAXSource;
        else
            return this;
    }

    /**
     * Return this DTM's lexical handler.
     *
     * %REVIEW% Should this return null if constrution already done/begun?
     *
     * @return null if this model doesn't respond to lexical SAX events,
     * "this" if the DTM object has a built-in SAX ContentHandler,
     * the IncrementalSAXSource if we're bound to one and should receive
     * the SAX stream via it for incremental build purposes...
     */
    public LexicalHandler getLexicalHandler() {

        if (m_incrSAXSource instanceof IncrementalSAXSource_Filter)
            return (LexicalHandler) m_incrSAXSource;
        else
            return this;
    }

    /**
     * Return this DTM's EntityResolver.
     *
     * @return null if this model doesn't respond to SAX entity ref events.
     */
    public org.xml.sax.EntityResolver getEntityResolver() {

        return null;
    }

    /**
     * Return this DTM's DTDHandler.
     *
     * @return null if this model doesn't respond to SAX dtd events.
     */
    public org.xml.sax.DTDHandler getDTDHandler() {

        return null;
    }

    /**
     * Return this DTM's ErrorHandler.
     *
     * @return null if this model doesn't respond to SAX error events.
     */
    public org.xml.sax.ErrorHandler getErrorHandler() {

        return null;
    }

    /**
     * Return this DTM's DeclHandler.
     *
     * @return null if this model doesn't respond to SAX Decl events.
     */
    public org.xml.sax.ext.DeclHandler getDeclHandler() {

        return null;
    }

    /** @return true iff we're building this model incrementally (eg
     * we're partnered with a IncrementalSAXSource) and thus require that the
     * transformation and the parse run simultaneously. Guidance to the
     * DTMManager.
     * */
    public boolean needsTwoThreads() {
        return null != m_incrSAXSource;
    }

    //================================================================
    // ========= SAX2 ContentHandler methods =========
    // Accept SAX events, use them to build/extend the DTM tree.
    // Replaces the deprecated DocumentHandler interface.

    public void characters(char[] ch, int start, int length) throws org.xml.sax.SAXException {
        // Actually creating the text node is handled by
        // processAccumulatedText(); here we just accumulate the
        // characters into the buffer.
        m_char.append(ch, start, length);
    }

    // Flush string accumulation into a text node
    private void processAccumulatedText() {
        int len = m_char.length();
        if (len != m_char_current_start) {
            // The FastStringBuffer has been previously agreed upon
            appendTextChild(m_char_current_start, len - m_char_current_start);
            m_char_current_start = len;
        }
    }

    public void endDocument() throws org.xml.sax.SAXException {
        // May need to tell the low-level builder code to pop up a level.
        // There _should't_ be any significant pending text at this point.
        appendEndDocument();
    }

    public void endElement(java.lang.String namespaceURI, java.lang.String localName, java.lang.String qName) throws org.xml.sax.SAXException {
        processAccumulatedText();
        // No args but we do need to tell the low-level builder code to
        // pop up a level.
        appendEndElement();
    }

    public void endPrefixMapping(java.lang.String prefix) throws org.xml.sax.SAXException {
        // No-op
    }

    public void ignorableWhitespace(char[] ch, int start, int length) throws org.xml.sax.SAXException {
        // %TBD% I believe ignorable text isn't part of the DTM model...?
    }

    public void processingInstruction(java.lang.String target, java.lang.String data) throws org.xml.sax.SAXException {
        processAccumulatedText();
        // %TBD% Which pools do target and data go into?
    }

    public void setDocumentLocator(Locator locator) {
        // No-op for DTM
    }

    public void skippedEntity(java.lang.String name) throws org.xml.sax.SAXException {
        processAccumulatedText();
        //%TBD%
    }

    public void startDocument() throws org.xml.sax.SAXException {
        appendStartDocument();
    }

    public void startElement(java.lang.String namespaceURI, java.lang.String localName, java.lang.String qName, Attributes atts) throws org.xml.sax.SAXException {
        processAccumulatedText();

        // %TBD% Split prefix off qname
        String prefix = null;
        int colon = qName.indexOf(':');
        if (colon > 0)
            prefix = qName.substring(0, colon);

        // %TBD% Where do we pool expandedName, or is it just the union, or...
        /**/System.out.println("Prefix=" + prefix + " index=" + m_prefixNames.stringToIndex(prefix));
        appendStartElement(m_nsNames.stringToIndex(namespaceURI), m_localNames.stringToIndex(localName), m_prefixNames.stringToIndex(prefix)); /////// %TBD%

        // %TBD% I'm assuming that DTM will require resequencing of
        // NS decls before other attrs, hence two passes are taken.
        // %TBD% Is there an easier way to test for NSDecl?
        int nAtts = (atts == null) ? 0 : atts.getLength();
        // %TBD% Countdown is more efficient if nobody cares about sequence.
        for (int i = nAtts - 1; i >= 0; --i) {
            qName = atts.getQName(i);
            if (qName.startsWith("xmlns:") || "xmlns".equals(qName)) {
                prefix = null;
                colon = qName.indexOf(':');
                if (colon > 0) {
                    prefix = qName.substring(0, colon);
                } else {
                    // %REVEIW% Null or ""?
                    prefix = null; // Default prefix
                }

                appendNSDeclaration(m_prefixNames.stringToIndex(prefix), m_nsNames.stringToIndex(atts.getValue(i)), atts.getType(i).equalsIgnoreCase("ID"));
            }
        }

        for (int i = nAtts - 1; i >= 0; --i) {
            qName = atts.getQName(i);
            if (!(qName.startsWith("xmlns:") || "xmlns".equals(qName))) {
                // %TBD% I hate having to extract the prefix into a new
                // string when we may never use it. Consider pooling whole
                // qNames, which are already strings?
                prefix = null;
                colon = qName.indexOf(':');
                if (colon > 0) {
                    prefix = qName.substring(0, colon);
                    localName = qName.substring(colon + 1);
                } else {
                    prefix = ""; // Default prefix
                    localName = qName;
                }

                m_char.append(atts.getValue(i)); // Single-string value
                int contentEnd = m_char.length();

                if (!("xmlns".equals(prefix) || "xmlns".equals(qName)))
                    appendAttribute(m_nsNames.stringToIndex(atts.getURI(i)), m_localNames.stringToIndex(localName), m_prefixNames.stringToIndex(prefix), atts.getType(i).equalsIgnoreCase("ID"), m_char_current_start, contentEnd - m_char_current_start);
                m_char_current_start = contentEnd;
            }
        }
    }

    public void startPrefixMapping(java.lang.String prefix, java.lang.String uri) throws org.xml.sax.SAXException {
        // No-op in DTM, handled during element/attr processing?
    }

    //
    // LexicalHandler support. Not all SAX2 parsers support these events
    // but we may want to pass them through when they exist...
    //
    public void comment(char[] ch, int start, int length) throws org.xml.sax.SAXException {
        processAccumulatedText();

        m_char.append(ch, start, length); // Single-string value
        appendComment(m_char_current_start, length);
        m_char_current_start += length;
    }

    public void endCDATA() throws org.xml.sax.SAXException {
        // No-op in DTM
    }

    public void endDTD() throws org.xml.sax.SAXException {
        // No-op in DTM
    }

    public void endEntity(java.lang.String name) throws org.xml.sax.SAXException {
        // No-op in DTM
    }

    public void startCDATA() throws org.xml.sax.SAXException {
        // No-op in DTM
    }

    public void startDTD(java.lang.String name, java.lang.String publicId, java.lang.String systemId) throws org.xml.sax.SAXException {
        // No-op in DTM
    }

    public void startEntity(java.lang.String name) throws org.xml.sax.SAXException {
        // No-op in DTM
    }

    //================================================================
    // ========= Document Handler Functions =========
    // %REVIEW% jjk -- DocumentHandler is  SAX Level 1, and deprecated....
    // and this wasn't a fully compliant or declared implementation of that API
    // in any case. Phase out in favor of SAX2 ContentHandler/LexicalHandler

    /**
     * Reset a dtm document to its initial (empty) state.
     *
     * The DTMManager will invoke this method when the dtm is created.
     *
     * @param documentNumber the handle for the DTM document.
     */
    final void initDocument(int documentNumber) {
        // save masked DTM document handle
        m_docHandle = documentNumber << DOCHANDLE_SHIFT;

        // Initialize the doc -- no parent, no next-sib
        nodes.writeSlot(0, DOCUMENT_NODE, -1, -1, 0);
        // wait for the first startElement to create the doc root node
        done = false;
    }

    //      /**
    //       * Receive hint of the end of a document.
    //       *
    //       * <p>The content handler will invoke this method only once, and it will
    //       * be the last method invoked during the parse.  The handler shall not
    //       * not invoke this method until it has either abandoned parsing
    //       * (because of an unrecoverable error) or reached the end of
    //       * input.</p>
    //       */
    //      public void documentEnd()
    //      {
    //              done = true;
    //              // %TBD% may need to notice the last slot number and slot count to avoid
    //              // residual data from provious use of this DTM
    //      }

    //      /**
    //       * Receive notification of the beginning of a document.
    //       *
    //       * <p>The SAX parser will invoke this method only once, before any
    //       * other methods in this interface.</p>
    //       */
    //      public void reset()
    //      {

    //              // %TBD% reset slot 0 to indicate ChunkedIntArray reuse or wait for
    //              //       the next initDocument().
    //              m_docElement = NULL;     // reset nodeHandle to the root of the actual dtm doc content
    //              initDocument(0);
    //      }

    //      /**
    //       * Factory method; creates an Element node in this document.
    //       *
    //       * The node created will be chained according to its natural order of request
    //       * received.  %TBD% It can be rechained later via the optional DTM writable interface.
    //       *
    //       * <p>The XML content handler will invoke endElement() method after all
    //       * of the element's content are processed in order to give DTM the indication
    //       * to prepare and patch up parent and sibling node pointers.</p>
    //       *
    //       * <p>The following interface for createElement will use an index value corresponds
    //       * to the symbol entry in the DTMDStringPool based symbol tables.</p>
    //       *
    //       * @param nsIndex The namespace of the node
    //       * @param nameIndex The element name.
    //       * @see #endElement
    //       * @see org.xml.sax.Attributes
    //       * @return nodeHandle int of the element created
    //       */
    //      public int createElement(int nsIndex, int nameIndex, Attributes atts)
    //      {
    //              // do document root node creation here on the first element, create nodes for
    //              // this element and its attributes, store the element, namespace, and attritute
    //              // name indexes to the nodes array, keep track of the current node and parent
    //              // element used

    //              // W0  High:  Namespace  Low:  Node Type
    //              int w0 = (nsIndex << 16) | ELEMENT_NODE;
    //              // W1: Parent
    //              int w1 = currentParent;
    //              // W2: Next  (initialized as 0)
    //              int w2 = 0;
    //              // W3: Tagname
    //              int w3 = nameIndex;
    //              //int ourslot = nodes.appendSlot(w0, w1, w2, w3);
    //              int ourslot = appendNode(w0, w1, w2, w3);
    //              currentParent = ourslot;
    //              previousSibling = 0;
    //              setAttributes(atts);

    //              // set the root element pointer when creating the first element node
    //              if (m_docElement == NULL)
    //                      m_docElement = ourslot;
    //              return (m_docHandle | ourslot);
    //      }

    //      // Factory method to create an Element node not associated with a given name space
    //      // using String value parameters passed in from a content handler or application
    //      /**
    //       * Factory method; creates an Element node not associated with a given name space in this document.
    //       *
    //       * The node created will be chained according to its natural order of request
    //       * received.  %TBD% It can be rechained later via the optional DTM writable interface.
    //       *
    //       * <p>The XML content handler or application will invoke endElement() method after all
    //       * of the element's content are processed in order to give DTM the indication
    //       * to prepare and patch up parent and sibling node pointers.</p>
    //       *
    //       * <p>The following parameters for createElement contains raw string values for name
    //       * symbols used in an Element node.</p>
    //       *
    //       * @param name String the element name, including the prefix if any.
    //       * @param atts The attributes attached to the element, if any.
    //       * @see #endElement
    //       * @see org.xml.sax.Attributes
    //       */
    //      public int createElement(String name, Attributes atts)
    //      {
    //              // This method wraps around the index valued interface of the createElement interface.
    //              // The raw string values are stored into the current DTM name symbol tables.  The method
    //              // method will then use the index values returned to invoke the other createElement()
    //              // onverted to index values modified to match a
    //              // method.
    //              int nsIndex = NULL;
    //              int nameIndex = m_localNames.stringToIndex(name);
    //              // note - there should be no prefix separator in the name because it is not associated
    //              // with a name space

    //              return createElement(nsIndex, nameIndex, atts);
    //      }

    //      // Factory method to create an Element node associated with a given name space
    //      // using String value parameters passed in from a content handler or application
    //      /**
    //       * Factory method; creates an Element node associated with a given name space in this document.
    //       *
    //       * The node created will be chained according to its natural order of request
    //       * received.  %TBD% It can be rechained later via the optional DTM writable interface.
    //       *
    //       * <p>The XML content handler or application will invoke endElement() method after all
    //       * of the element's content are processed in order to give DTM the indication
    //       * to prepare and patch up parent and sibling node pointers.</p>
    //       *
    //       * <p>The following parameters for createElementNS contains raw string values for name
    //       * symbols used in an Element node.</p>
    //       *
    //       * @param ns String the namespace of the node
    //       * @param name String the element name, including the prefix if any.
    //       * @param atts The attributes attached to the element, if any.
    //       * @see #endElement
    //       * @see org.xml.sax.Attributes
    //       */
    //      public int createElementNS(String ns, String name, Attributes atts)
    //      {
    //              // This method wraps around the index valued interface of the createElement interface.
    //              // The raw string values are stored into the current DTM name symbol tables.  The method
    //              // method will then use the index values returned to invoke the other createElement()
    //              // onverted to index values modified to match a
    //              // method.
    //              int nsIndex = m_nsNames.stringToIndex(ns);
    //              int nameIndex = m_localNames.stringToIndex(name);
    //              // The prefixIndex is not needed by the indexed interface of the createElement method
    //              int prefixSep = name.indexOf(":");
    //              int prefixIndex = m_prefixNames.stringToIndex(name.substring(0, prefixSep));
    //              return createElement(nsIndex, nameIndex, atts);
    //      }

    //      /**
    //       * Receive an indication for the end of an element.
    //       *
    //       * <p>The XML content handler will invoke this method at the end of every
    //       * element in the XML document to give hint its time to pop up the current
    //       * element and parent and patch up parent and sibling pointers if necessary
    //       *
    //       * <p>%tbd% The following interface may need to be modified to match a
    //       * coordinated access to the DTMDStringPool based symbol tables.</p>
    //               *
    //       * @param ns the namespace of the element
    //       * @param name The element name
    //       */
    //      public void endElement(String ns, String name)
    //      {
    //              // pop up the stacks

    //              //
    //              if (previousSiblingWasParent)
    //                      nodes.writeEntry(previousSibling, 2, NULL);

    //              // Pop parentage
    //              previousSibling = currentParent;
    //              nodes.readSlot(currentParent, gotslot);
    //              currentParent = gotslot[1] & 0xFFFF;

    //              // The element just being finished will be
    //              // the previous sibling for the next operation
    //              previousSiblingWasParent = true;

    //              // Pop a level of namespace table
    //              // namespaceTable.removeLastElem();
    //      }

    //      /**
    //       * Creates attributes for the current node.
    //       *
    //       * @param atts Attributes to be created.
    //       */
    //      void setAttributes(Attributes atts) {
    //              int atLength = (null == atts) ? 0 : atts.getLength();
    //              for (int i=0; i < atLength; i++) {
    //                      String qname = atts.getQName(i);
    //                      createAttribute(atts.getQName(i), atts.getValue(i));
    //              }
    //      }

    //      /**
    //       * Appends an attribute to the document.
    //       * @param qname Qualified Name of the attribute
    //       * @param value Value of the attribute
    //       * @return Handle of node
    //       */
    //      public int createAttribute(String qname, String value) {
    //              int colonpos = qname.indexOf(":");
    //              String attName = qname.substring(colonpos+1);
    //              int w0 = 0;
    //              if (colonpos > 0) {
    //                      String prefix = qname.substring(0, colonpos);
    //                      if (prefix.equals("xml")) {
    //                              //w0 = ATTRIBUTE_NODE |
    //                              //      (com.sun.org.apache.xalan.internal.templates.Constants.S_XMLNAMESPACEURI << 16);
    //                      } else {
    //                              //w0 = ATTRIBUTE_NODE |
    //                      }
    //              } else {
    //                      w0 = ATTRIBUTE_NODE;
    //              }
    //              // W1:  Parent
    //              int w1 = currentParent;
    //              // W2:  Next (not yet resolved)
    //              int w2 = 0;
    //              // W3:  Tag name
    //              int w3 = m_localNames.stringToIndex(attName);
    //              // Add node
    //              int ourslot = appendNode(w0, w1, w2, w3);
    //              previousSibling = ourslot;      // Should attributes be previous siblings

    //              // W0: Node Type
    //              w0 = TEXT_NODE;
    //              // W1: Parent
    //              w1 = ourslot;
    //              // W2: Start Position within buffer
    //              w2 = m_char.length();
    //              m_char.append(value);
    //              // W3: Length
    //              w3 = m_char.length() - w2;
    //              appendNode(w0, w1, w2, w3);
    //              charStringStart=m_char.length();
    //              charStringLength = 0;
    //              //previousSibling = ourslot;
    //              // Attrs are Parents
    //              previousSiblingWasParent = true;
    //              return (m_docHandle | ourslot);
    //      }

    //      /**
    //       * Factory method; creates a Text node in this document.
    //       *
    //       * The node created will be chained according to its natural order of request
    //       * received.  %TBD% It can be rechained later via the optional DTM writable interface.
    //       *
    //       * @param text String The characters text string from the XML document.
    //       * @return int DTM node-number of the text node created
    //       */
    //      public int createTextNode(String text)
    //      throws DTMException
    //      {
    //              // wraps around the index value based createTextNode method
    //              return createTextNode(text.toCharArray(), 0, text.length());
    //      }

    //      /**
    //       * Factory method; creates a Text node in this document.
    //       *
    //       * The node created will be chained according to its natural order of request
    //       * received.  %TBD% It can be rechained later via the optional DTM writable interface.
    //       *
    //       * %REVIEW% for text normalization issues, unless we are willing to
    //       * insist that all adjacent text must be merged before this method
    //       * is called.
    //       *
    //       * @param ch The characters from the XML document.
    //       * @param start The start position in the array.
    //       * @param length The number of characters to read from the array.
    //       */
    //      public int createTextNode(char ch[], int start, int length)
    //      throws DTMException
    //      {
    //              m_char.append(ch, start, length);               // store the chunk to the text/comment string table

    //              // create a Text Node
    //              // %TBD% may be possible to combine with appendNode()to replace the next chunk of code
    //              int w0 = TEXT_NODE;
    //              // W1: Parent
    //              int w1 = currentParent;
    //              // W2: Start position within m_char
    //              int w2 = charStringStart;
    //              // W3: Length of the full string
    //              int w3 = length;
    //              int ourslot = appendNode(w0, w1, w2, w3);
    //              previousSibling = ourslot;

    //              charStringStart=m_char.length();
    //              charStringLength = 0;
    //              return (m_docHandle | ourslot);
    //      }

    //      /**
    //       * Factory method; creates a Comment node in this document.
    //       *
    //       * The node created will be chained according to its natural order of request
    //       * received.  %TBD% It can be rechained later via the optional DTM writable interface.
    //       *
    //       * @param text String The characters text string from the XML document.
    //       * @return int DTM node-number of the text node created
    //       */
    //      public int createComment(String text)
    //      throws DTMException
    //      {
    //              // wraps around the index value based createTextNode method
    //              return createComment(text.toCharArray(), 0, text.length());
    //      }

    //      /**
    //       * Factory method; creates a Comment node in this document.
    //       *
    //       * The node created will be chained according to its natural order of request
    //       * received.  %TBD% It can be rechained later via the optional DTM writable interface.
    //       *
    //       * @param ch An array holding the characters in the comment.
    //       * @param start The starting position in the array.
    //       * @param length The number of characters to use from the array.
    //       * @see DTMException
    //       */
    //      public int createComment(char ch[], int start, int length)
    //      throws DTMException
    //      {
    //              m_char.append(ch, start, length);               // store the comment string to the text/comment string table

    //              // create a Comment Node
    //              // %TBD% may be possible to combine with appendNode()to replace the next chunk of code
    //              int w0 = COMMENT_NODE;
    //              // W1: Parent
    //              int w1 = currentParent;
    //              // W2: Start position within m_char
    //              int w2 = charStringStart;
    //              // W3: Length of the full string
    //              int w3 = length;
    //              int ourslot = appendNode(w0, w1, w2, w3);
    //              previousSibling = ourslot;

    //              charStringStart=m_char.length();
    //              charStringLength = 0;
    //              return (m_docHandle | ourslot);
    //      }

    //      // Counters to keep track of the current text string being accumulated with respect
    //      // to the text/comment string table: charStringStart should point to the starting
    //      // offset of the string in the table and charStringLength the acccumulated length when
    //      // appendAccumulatedText starts, and reset to the end of the table and 0 at the end
    //      // of appendAccumulatedText for the next set of characters receives
    //      int charStringStart=0,charStringLength=0;

    // ========= Document Navigation Functions =========

    /** Given a node handle, test if it has child nodes.
     * <p> %REVIEW% This is obviously useful at the DOM layer, where it
     * would permit testing this without having to create a proxy
     * node. It's less useful in the DTM API, where
     * (dtm.getFirstChild(nodeHandle)!=DTM.NULL) is just as fast and
     * almost as self-evident. But it's a convenience, and eases porting
     * of DOM code to DTM.  </p>
     *
     * @param nodeHandle int Handle of the node.
     * @return int true if the given node has child nodes.
     */
    public boolean hasChildNodes(int nodeHandle) {
        return (getFirstChild(nodeHandle) != NULL);
    }

    /**
     * Given a node handle, get the handle of the node's first child.
     * If not yet resolved, waits for more nodes to be added to the document and
     * tries again.
     *
     * @param nodeHandle int Handle of the node.
     * @return int DTM node-number of first child, or DTM.NULL to indicate none exists.
     */
    public int getFirstChild(int nodeHandle) {

        // ###shs worry about tracing/debug later
        nodeHandle &= NODEHANDLE_MASK;
        // Read node into variable
        nodes.readSlot(nodeHandle, gotslot);

        // type is the last half of first slot
        short type = (short) (gotslot[0] & 0xFFFF);

        // Check to see if Element or Document node
        if ((type == ELEMENT_NODE) || (type == DOCUMENT_NODE) || (type == ENTITY_REFERENCE_NODE)) {

            // In case when Document root is given
            //      if (nodeHandle == 0) nodeHandle = 1;
            // %TBD% Probably was a mistake.
            // If someone explicitly asks for first child
            // of Document, I would expect them to want
            // that and only that.

            int kid = nodeHandle + 1;
            nodes.readSlot(kid, gotslot);
            while (ATTRIBUTE_NODE == (gotslot[0] & 0xFFFF)) {
                // points to next sibling
                kid = gotslot[2];
                // Return NULL if node has only attributes
                if (kid == NULL)
                    return NULL;
                nodes.readSlot(kid, gotslot);
            }
            // If parent slot matches given parent, return kid
            if (gotslot[1] == nodeHandle) {
                int firstChild = kid | m_docHandle;

                return firstChild;
            }
        }
        // No child found

        return NULL;
    }

    /**
    * Given a node handle, advance to its last child.
    * If not yet resolved, waits for more nodes to be added to the document and
    * tries again.
    *
    * @param nodeHandle int Handle of the node.
    * @return int Node-number of last child,
    * or DTM.NULL to indicate none exists.
    */
    public int getLastChild(int nodeHandle) {
        // ###shs put trace/debug later
        nodeHandle &= NODEHANDLE_MASK;
        // do not need to test node type since getFirstChild does that
        int lastChild = NULL;
        for (int nextkid = getFirstChild(nodeHandle); nextkid != NULL; nextkid = getNextSibling(nextkid)) {
            lastChild = nextkid;
        }
        return lastChild | m_docHandle;
    }

    /**
     * Retrieves an attribute node by by qualified name and namespace URI.
     *
     * @param nodeHandle int Handle of the node upon which to look up this attribute.
     * @param namespaceURI The namespace URI of the attribute to
     *   retrieve, or null.
     * @param name The local name of the attribute to
     *   retrieve.
     * @return The attribute node handle with the specified name (
     *   <code>nodeName</code>) or <code>DTM.NULL</code> if there is no such
     *   attribute.
     */
    public int getAttributeNode(int nodeHandle, String namespaceURI, String name) {
        int nsIndex = m_nsNames.stringToIndex(namespaceURI),
                        nameIndex = m_localNames.stringToIndex(name);
        nodeHandle &= NODEHANDLE_MASK;
        nodes.readSlot(nodeHandle, gotslot);
        short type = (short) (gotslot[0] & 0xFFFF);
        // If nodeHandle points to element next slot would be first attribute
        if (type == ELEMENT_NODE)
            nodeHandle++;
        // Iterate through Attribute Nodes
        while (type == ATTRIBUTE_NODE) {
            if ((nsIndex == (gotslot[0] << 16)) && (gotslot[3] == nameIndex))
                return nodeHandle | m_docHandle;
            // Goto next sibling
            nodeHandle = gotslot[2];
            nodes.readSlot(nodeHandle, gotslot);
        }
        return NULL;
    }

    /**
     * Given a node handle, get the index of the node's first attribute.
     *
     * @param nodeHandle int Handle of the Element node.
     * @return Handle of first attribute, or DTM.NULL to indicate none exists.
     */
    public int getFirstAttribute(int nodeHandle) {
        nodeHandle &= NODEHANDLE_MASK;

        // %REVIEW% jjk: Just a quick observation: If you're going to
        // call readEntry repeatedly on the same node, it may be
        // more efficiently to do a readSlot to get the data locally,
        // reducing the addressing and call-and-return overhead.

        // Should we check if handle is element (do we want sanity checks?)
        if (ELEMENT_NODE != (nodes.readEntry(nodeHandle, 0) & 0xFFFF))
            return NULL;
        // First Attribute (if any) should be at next position in table
        nodeHandle++;
        return (ATTRIBUTE_NODE == (nodes.readEntry(nodeHandle, 0) & 0xFFFF)) ? nodeHandle | m_docHandle : NULL;
    }

    /**
     * Given a node handle, get the index of the node's first child.
     * If not yet resolved, waits for more nodes to be added to the document and
     * tries again
     *
     * @param nodeHandle handle to node, which should probably be an element
     *                   node, but need not be.
     *
     * @param inScope    true if all namespaces in scope should be returned,
     *                   false if only the namespace declarations should be
     *                   returned.
     * @return handle of first namespace, or DTM.NULL to indicate none exists.
     */
    public int getFirstNamespaceNode(int nodeHandle, boolean inScope) {

        return NULL;
    }

    /**
     * Given a node handle, advance to its next sibling.
     *
     * %TBD% This currently uses the DTM-internal definition of
     * sibling; eg, the last attr's next sib is the first
     * child. In the old DTM, the DOM proxy layer provided the
     * additional logic for the public view.  If we're rewriting
     * for XPath emulation, that test must be done here.
     *
     * %TBD% CODE INTERACTION WITH INCREMENTAL PARSE - If not yet
     * resolved, should wait for more nodes to be added to the document
     * and tries again.
     *
     * @param nodeHandle int Handle of the node.
     * @return int Node-number of next sibling,
     * or DTM.NULL to indicate none exists.
     * */
    public int getNextSibling(int nodeHandle) {
        nodeHandle &= NODEHANDLE_MASK;
        // Document root has no next sibling
        if (nodeHandle == 0)
            return NULL;

        short type = (short) (nodes.readEntry(nodeHandle, 0) & 0xFFFF);
        if ((type == ELEMENT_NODE) || (type == ATTRIBUTE_NODE) || (type == ENTITY_REFERENCE_NODE)) {
            int nextSib = nodes.readEntry(nodeHandle, 2);
            if (nextSib == NULL)
                return NULL;
            if (nextSib != 0)
                return (m_docHandle | nextSib);
            // ###shs should cycle/wait if nextSib is 0? Working on threading next
        }
        // Next Sibling is in the next position if it shares the same parent
        int thisParent = nodes.readEntry(nodeHandle, 1);

        if (nodes.readEntry(++nodeHandle, 1) == thisParent)
            return (m_docHandle | nodeHandle);

        return NULL;
    }

    /**
     * Given a node handle, find its preceeding sibling.
     * WARNING: DTM is asymmetric; this operation is resolved by search, and is
     * relatively expensive.
     *
     * @param nodeHandle the id of the node.
     * @return int Node-number of the previous sib,
     * or DTM.NULL to indicate none exists.
     */
    public int getPreviousSibling(int nodeHandle) {
        nodeHandle &= NODEHANDLE_MASK;
        // Document root has no previous sibling
        if (nodeHandle == 0)
            return NULL;

        int parent = nodes.readEntry(nodeHandle, 1);
        int kid = NULL;
        for (int nextkid = getFirstChild(parent); nextkid != nodeHandle; nextkid = getNextSibling(nextkid)) {
            kid = nextkid;
        }
        return kid | m_docHandle;
    }

    /**
     * Given a node handle, advance to the next attribute. If an
     * element, we advance to its first attribute; if an attr, we advance to
     * the next attr on the same node.
     *
     * @param nodeHandle int Handle of the node.
     * @return int DTM node-number of the resolved attr,
     * or DTM.NULL to indicate none exists.
     */
    public int getNextAttribute(int nodeHandle) {
        nodeHandle &= NODEHANDLE_MASK;
        nodes.readSlot(nodeHandle, gotslot);

        //%REVIEW% Why are we using short here? There's no storage
        //reduction for an automatic variable, especially one used
        //so briefly, and it typically costs more cycles to process
        //than an int would.
        short type = (short) (gotslot[0] & 0xFFFF);

        if (type == ELEMENT_NODE) {
            return getFirstAttribute(nodeHandle);
        } else if (type == ATTRIBUTE_NODE) {
            if (gotslot[2] != NULL)
                return (m_docHandle | gotslot[2]);
        }
        return NULL;
    }

    /**
     * Given a namespace handle, advance to the next namespace.
     *
     * %TBD% THIS METHOD DOES NOT MATCH THE CURRENT SIGNATURE IN
     * THE DTM INTERFACE.  FIX IT, OR JUSTIFY CHANGING THE DTM
     * API.
     *
     * @param namespaceHandle handle to node which must be of type NAMESPACE_NODE.
     * @return handle of next namespace, or DTM.NULL to indicate none exists.
     */
    public int getNextNamespaceNode(int baseHandle, int namespaceHandle, boolean inScope) {
        // ###shs need to work on namespace
        return NULL;
    }

    /**
     * Given a node handle, advance to its next descendant.
     * If not yet resolved, waits for more nodes to be added to the document and
     * tries again.
     *
     * @param subtreeRootHandle
     * @param nodeHandle int Handle of the node.
     * @return handle of next descendant,
     * or DTM.NULL to indicate none exists.
     */
    public int getNextDescendant(int subtreeRootHandle, int nodeHandle) {
        subtreeRootHandle &= NODEHANDLE_MASK;
        nodeHandle &= NODEHANDLE_MASK;
        // Document root [Document Node? -- jjk] - no next-sib
        if (nodeHandle == 0)
            return NULL;
        while (!m_isError) {
            // Document done and node out of bounds
            if (done && (nodeHandle > nodes.slotsUsed()))
                break;
            if (nodeHandle > subtreeRootHandle) {
                nodes.readSlot(nodeHandle + 1, gotslot);
                if (gotslot[2] != 0) {
                    short type = (short) (gotslot[0] & 0xFFFF);
                    if (type == ATTRIBUTE_NODE) {
                        nodeHandle += 2;
                    } else {
                        int nextParentPos = gotslot[1];
                        if (nextParentPos >= subtreeRootHandle)
                            return (m_docHandle | (nodeHandle + 1));
                        else
                            break;
                    }
                } else if (!done) {
                    // Add wait logic here
                } else
                    break;
            } else {
                nodeHandle++;
            }
        }
        // Probably should throw error here like original instead of returning
        return NULL;
    }

    /**
     * Given a node handle, advance to the next node on the following axis.
     *
     * @param axisContextHandle the start of the axis that is being traversed.
     * @param nodeHandle
     * @return handle of next sibling,
     * or DTM.NULL to indicate none exists.
     */
    public int getNextFollowing(int axisContextHandle, int nodeHandle) {
        //###shs still working on
        return NULL;
    }

    /**
     * Given a node handle, advance to the next node on the preceding axis.
     *
     * @param axisContextHandle the start of the axis that is being traversed.
     * @param nodeHandle the id of the node.
     * @return int Node-number of preceding sibling,
     * or DTM.NULL to indicate none exists.
     */
    public int getNextPreceding(int axisContextHandle, int nodeHandle) {
        // ###shs copied from Xalan 1, what is this suppose to do?
        nodeHandle &= NODEHANDLE_MASK;
        while (nodeHandle > 1) {
            nodeHandle--;
            if (ATTRIBUTE_NODE == (nodes.readEntry(nodeHandle, 0) & 0xFFFF))
                continue;

            // if nodeHandle is _not_ an ancestor of
            // axisContextHandle, specialFind will return it.
            // If it _is_ an ancestor, specialFind will return -1

            // %REVIEW% unconditional return defeats the
            // purpose of the while loop -- does this
            // logic make any sense?

            return (m_docHandle | nodes.specialFind(axisContextHandle, nodeHandle));
        }
        return NULL;
    }

    /**
     * Given a node handle, find its parent node.
     *
     * @param nodeHandle the id of the node.
     * @return int Node-number of parent,
     * or DTM.NULL to indicate none exists.
     */
    public int getParent(int nodeHandle) {
        // Should check to see within range?

        // Document Root should not have to be handled differently
        return (m_docHandle | nodes.readEntry(nodeHandle, 1));
    }

    /**
     * Returns the root element of the document.
     * @return nodeHandle to the Document Root.
     */
    public int getDocumentRoot() {
        return (m_docHandle | m_docElement);
    }

    /**
     * Given a node handle, find the owning document node.
     *
     * @return int Node handle of document, which should always be valid.
     */
    public int getDocument() {
        return m_docHandle;
    }

    /**
     * Given a node handle, find the owning document node.  This has the exact
     * same semantics as the DOM Document method of the same name, in that if
     * the nodeHandle is a document node, it will return NULL.
     *
     * <p>%REVIEW% Since this is DOM-specific, it may belong at the DOM
     * binding layer. Included here as a convenience function and to
     * aid porting of DOM code to DTM.</p>
     *
     * @param nodeHandle the id of the node.
     * @return int Node handle of owning document, or NULL if the nodeHandle is
     *             a document.
     */
    public int getOwnerDocument(int nodeHandle) {
        // Assumption that Document Node is always in 0 slot
        if ((nodeHandle & NODEHANDLE_MASK) == 0)
            return NULL;
        return (nodeHandle & DOCHANDLE_MASK);
    }

    /**
     * Given a node handle, find the owning document node.  This has the DTM
     * semantics; a Document node is its own owner.
     *
     * <p>%REVIEW% Since this is DOM-specific, it may belong at the DOM
     * binding layer. Included here as a convenience function and to
     * aid porting of DOM code to DTM.</p>
     *
     * @param nodeHandle the id of the node.
     * @return int Node handle of owning document, or NULL if the nodeHandle is
     *             a document.
     */
    public int getDocumentRoot(int nodeHandle) {
        // Assumption that Document Node is always in 0 slot
        if ((nodeHandle & NODEHANDLE_MASK) == 0)
            return NULL;
        return (nodeHandle & DOCHANDLE_MASK);
    }

    /**
     * Get the string-value of a node as a String object
     * (see http://www.w3.org/TR/xpath#data-model
     * for the definition of a node's string-value).
     *
     * @param nodeHandle The node ID.
     *
     * @return A string object that represents the string-value of the given node.
     */
    public XMLString getStringValue(int nodeHandle) {
        // ###zaj - researching
        nodes.readSlot(nodeHandle, gotslot);
        int nodetype = gotslot[0] & 0xFF;
        String value = null;

        switch (nodetype) {
            case TEXT_NODE:
            case COMMENT_NODE:
            case CDATA_SECTION_NODE:
                value = m_char.getString(gotslot[2], gotslot[3]);
                break;
            case PROCESSING_INSTRUCTION_NODE:
            case ATTRIBUTE_NODE:
            case ELEMENT_NODE:
            case ENTITY_REFERENCE_NODE:
            default:
                break;
        }
        return m_xsf.newstr(value);

    }

    /**
     * Get number of character array chunks in
     * the string-value of a node.
     * (see http://www.w3.org/TR/xpath#data-model
     * for the definition of a node's string-value).
     * Note that a single text node may have multiple text chunks.
     *
     * EXPLANATION: This method is an artifact of the fact that the
     * underlying m_chars object may not store characters in a
     * single contiguous array -- for example,the current
     * FastStringBuffer may split a single node's text across
     * multiple allocation units.  This call tells us how many
     * separate accesses will be required to retrieve the entire
     * content. PLEASE NOTE that this may not be the same as the
     * number of SAX characters() events that caused the text node
     * to be built in the first place, since m_chars buffering may
     * be on different boundaries than the parser's buffers.
     *
     * @param nodeHandle The node ID.
     *
     * @return number of character array chunks in
     *         the string-value of a node.
     * */
    //###zaj - tbd
    public int getStringValueChunkCount(int nodeHandle) {
        //###zaj    return value
        return 0;
    }

    /**
     * Get a character array chunk in the string-value of a node.
     * (see http://www.w3.org/TR/xpath#data-model
     * for the definition of a node's string-value).
     * Note that a single text node may have multiple text chunks.
     *
     * EXPLANATION: This method is an artifact of the fact that
     * the underlying m_chars object may not store characters in a
     * single contiguous array -- for example,the current
     * FastStringBuffer may split a single node's text across
     * multiple allocation units.  This call retrieves a single
     * contiguous portion of the text -- as much as m-chars was
     * able to store in a single allocation unit.  PLEASE NOTE
     * that this may not be the same granularityas the SAX
     * characters() events that caused the text node to be built
     * in the first place, since m_chars buffering may be on
     * different boundaries than the parser's buffers.
     *
     * @param nodeHandle The node ID.
     * @param chunkIndex Which chunk to get.
     * @param startAndLen An array of 2 where the start position and length of
     *                    the chunk will be returned.
     *
     * @return The character array reference where the chunk occurs.  */
    //###zaj - tbd
    public char[] getStringValueChunk(int nodeHandle, int chunkIndex, int[] startAndLen) {
        return new char[0];
    }

    /**
     * Given a node handle, return an ID that represents the node's expanded name.
     *
     * @param nodeHandle The handle to the node in question.
     *
     * @return the expanded-name id of the node.
     */
    public int getExpandedTypeID(int nodeHandle) {
        nodes.readSlot(nodeHandle, gotslot);
        String qName = m_localNames.indexToString(gotslot[3]);
        // Remove prefix from qName
        // %TBD% jjk This is assuming the elementName is the qName.
        int colonpos = qName.indexOf(":");
        String localName = qName.substring(colonpos + 1);
        // Get NS
        String namespace = m_nsNames.indexToString(gotslot[0] << 16);
        // Create expanded name
        String expandedName = namespace + ":" + localName;
        int expandedNameID = m_nsNames.stringToIndex(expandedName);

        return expandedNameID;
    }

    /**
     * Given an expanded name, return an ID.  If the expanded-name does not
     * exist in the internal tables, the entry will be created, and the ID will
     * be returned.  Any additional nodes that are created that have this
     * expanded name will use this ID.
     *
     * @return the expanded-name id of the node.
     */
    public int getExpandedTypeID(String namespace, String localName, int type) {
        // Create expanded name
        // %TBD% jjk Expanded name is bitfield-encoded as
        // typeID[6]nsuriID[10]localID[16]. Switch to that form, and to
        // accessing the ns/local via their tables rather than confusing
        // nsnames and expandednames.
        String expandedName = namespace + ":" + localName;
        int expandedNameID = m_nsNames.stringToIndex(expandedName);

        return expandedNameID;
    }

    /**
     * Given an expanded-name ID, return the local name part.
     *
     * @param ExpandedNameID an ID that represents an expanded-name.
     * @return String Local name of this node.
     */
    public String getLocalNameFromExpandedNameID(int ExpandedNameID) {

        // Get expanded name
        String expandedName = m_localNames.indexToString(ExpandedNameID);
        // Remove prefix from expanded name
        int colonpos = expandedName.indexOf(":");
        String localName = expandedName.substring(colonpos + 1);
        return localName;
    }

    /**
     * Given an expanded-name ID, return the namespace URI part.
     *
     * @param ExpandedNameID an ID that represents an expanded-name.
     * @return String URI value of this node's namespace, or null if no
     * namespace was resolved.
    */
    public String getNamespaceFromExpandedNameID(int ExpandedNameID) {

        String expandedName = m_localNames.indexToString(ExpandedNameID);
        // Remove local name from expanded name
        int colonpos = expandedName.indexOf(":");
        String nsName = expandedName.substring(0, colonpos);

        return nsName;
    }

    /**
     * fixednames
    */
    private static final String[] fixednames = { null, null, // nothing, Element
    null, "#text", // Attr, Text
    "#cdata_section", null, // CDATA, EntityReference
    null, null, // Entity, PI
    "#comment", "#document", // Comment, Document
    null, "#document-fragment", // Doctype, DocumentFragment
    null }; // Notation

    /**
     * Given a node handle, return its DOM-style node name. This will
     * include names such as #text or #document.
     *
     * @param nodeHandle the id of the node.
     * @return String Name of this node, which may be an empty string.
     * %REVIEW% Document when empty string is possible...
     */
    public String getNodeName(int nodeHandle) {
        nodes.readSlot(nodeHandle, gotslot);
        short type = (short) (gotslot[0] & 0xFFFF);
        String name = fixednames[type];
        if (null == name) {
            int i = gotslot[3];
            /**/System.out.println("got i=" + i + " " + (i >> 16) + "/" + (i & 0xffff));

            name = m_localNames.indexToString(i & 0xFFFF);
            String prefix = m_prefixNames.indexToString(i >> 16);
            if (prefix != null && prefix.length() > 0)
                name = prefix + ":" + name;
        }
        return name;
    }

    /**
     * Given a node handle, return the XPath node name.  This should be
     * the name as described by the XPath data model, NOT the DOM-style
     * name.
     *
     * @param nodeHandle the id of the node.
     * @return String Name of this node.
     */
    public String getNodeNameX(int nodeHandle) {
        return null;
    }

    /**
     * Given a node handle, return its DOM-style localname.
     * (As defined in Namespaces, this is the portion of the name after any
     * colon character)
     *
     * %REVIEW% What's the local name of something other than Element/Attr?
     * Should this be DOM-style (undefined unless namespaced), or other?
     *
     * @param nodeHandle the id of the node.
     * @return String Local name of this node.
     */
    public String getLocalName(int nodeHandle) {
        nodes.readSlot(nodeHandle, gotslot);
        short type = (short) (gotslot[0] & 0xFFFF);
        String name = "";
        if ((type == ELEMENT_NODE) || (type == ATTRIBUTE_NODE)) {
            int i = gotslot[3];
            name = m_localNames.indexToString(i & 0xFFFF);
            if (name == null)
                name = "";
        }
        return name;
    }

    /**
     * Given a namespace handle, return the prefix that the namespace decl is
     * mapping.
     * Given a node handle, return the prefix used to map to the namespace.
     *
     * <p> %REVIEW% Are you sure you want "" for no prefix?  </p>
     *
     * %REVIEW%  Should this be DOM-style (undefined unless namespaced),
     * or other?
     *
     * @param nodeHandle the id of the node.
     * @return String prefix of this node's name, or "" if no explicit
     * namespace prefix was given.
     */
    public String getPrefix(int nodeHandle) {
        nodes.readSlot(nodeHandle, gotslot);
        short type = (short) (gotslot[0] & 0xFFFF);
        String name = "";
        if ((type == ELEMENT_NODE) || (type == ATTRIBUTE_NODE)) {
            int i = gotslot[3];
            name = m_prefixNames.indexToString(i >> 16);
            if (name == null)
                name = "";
        }
        return name;
    }

    /**
     * Given a node handle, return its DOM-style namespace URI
     * (As defined in Namespaces, this is the declared URI which this node's
     * prefix -- or default in lieu thereof -- was mapped to.)
     *
     * @param nodeHandle the id of the node.
     * @return String URI value of this node's namespace, or null if no
     * namespace was resolved.
     */
    public String getNamespaceURI(int nodeHandle) {
        return null;
    }

    /**
     * Given a node handle, return its node value. This is mostly
     * as defined by the DOM, but may ignore some conveniences.
     * <p>
     *
     * @param nodeHandle The node id.
     * @return String Value of this node, or null if not
     * meaningful for this node type.
     */
    public String getNodeValue(int nodeHandle) {
        nodes.readSlot(nodeHandle, gotslot);
        int nodetype = gotslot[0] & 0xFF; // ###zaj use mask to get node type
        String value = null;

        switch (nodetype) { // ###zaj todo - document nodetypes
            case ATTRIBUTE_NODE:
                nodes.readSlot(nodeHandle + 1, gotslot);
            case TEXT_NODE:
            case COMMENT_NODE:
            case CDATA_SECTION_NODE:
                value = m_char.getString(gotslot[2], gotslot[3]); //###zaj
                break;
            case PROCESSING_INSTRUCTION_NODE:
            case ELEMENT_NODE:
            case ENTITY_REFERENCE_NODE:
            default:
                break;
        }
        return value;
    }

    /**
     * Given a node handle, return its DOM-style node type.
     * <p>
     * %REVIEW% Generally, returning short is false economy. Return int?
     *
     * @param nodeHandle The node id.
     * @return int Node type, as per the DOM's Node._NODE constants.
     */
    public short getNodeType(int nodeHandle) {
        return (short) (nodes.readEntry(nodeHandle, 0) & 0xFFFF);
    }

    /**
     * Get the depth level of this node in the tree (equals 1 for
     * a parentless node).
     *
     * @param nodeHandle The node id.
     * @return the number of ancestors, plus one
     * @xsl.usage internal
     */
    public short getLevel(int nodeHandle) {
        short count = 0;
        while (nodeHandle != 0) {
            count++;
            nodeHandle = nodes.readEntry(nodeHandle, 1);
        }
        return count;
    }

    // ============== Document query functions ==============

    /**
     * Tests whether DTM DOM implementation implements a specific feature and
     * that feature is supported by this node.
     *
     * @param feature The name of the feature to test.
     * @param version This is the version number of the feature to test.
     *   If the version is not
     *   specified, supporting any version of the feature will cause the
     *   method to return <code>true</code>.
     * @return Returns <code>true</code> if the specified feature is
     *   supported on this node, <code>false</code> otherwise.
     */
    public boolean isSupported(String feature, String version) {
        return false;
    }

    /**
     * Return the base URI of the document entity. If it is not known
     * (because the document was parsed from a socket connection or from
     * standard input, for example), the value of this property is unknown.
     *
     * @return the document base URI String object or null if unknown.
     */
    public String getDocumentBaseURI() {

        return m_documentBaseURI;
    }

    /**
     * Set the base URI of the document entity.
     *
     * @param baseURI the document base URI String object or null if unknown.
     */
    public void setDocumentBaseURI(String baseURI) {

        m_documentBaseURI = baseURI;
    }

    /**
     * Return the system identifier of the document entity. If
     * it is not known, the value of this property is unknown.
     *
     * @param nodeHandle The node id, which can be any valid node handle.
     * @return the system identifier String object or null if unknown.
     */
    public String getDocumentSystemIdentifier(int nodeHandle) {
        return null;
    }

    /**
     * Return the name of the character encoding scheme
     *        in which the document entity is expressed.
     *
     * @param nodeHandle The node id, which can be any valid node handle.
     * @return the document encoding String object.
     */
    public String getDocumentEncoding(int nodeHandle) {
        return null;
    }

    /**
     * Return an indication of the standalone status of the document,
     *        either "yes" or "no". This property is derived from the optional
     *        standalone document declaration in the XML declaration at the
     *        beginning of the document entity, and has no value if there is no
     *        standalone document declaration.
     *
     * @param nodeHandle The node id, which can be any valid node handle.
     * @return the document standalone String object, either "yes", "no", or null.
     */
    public String getDocumentStandalone(int nodeHandle) {
        return null;
    }

    /**
     * Return a string representing the XML version of the document. This
     * property is derived from the XML declaration optionally present at the
     * beginning of the document entity, and has no value if there is no XML
     * declaration.
     *
     * @param documentHandle the document handle
     *
     * @return the document version String object
     */
    public String getDocumentVersion(int documentHandle) {
        return null;
    }

    /**
     * Return an indication of
     * whether the processor has read the complete DTD. Its value is a
     * boolean. If it is false, then certain properties (indicated in their
     * descriptions below) may be unknown. If it is true, those properties
     * are never unknown.
     *
     * @return <code>true</code> if all declarations were processed {};
     *         <code>false</code> otherwise.
     */
    public boolean getDocumentAllDeclarationsProcessed() {
        return false;
    }

    /**
     *   A document type declaration information item has the following properties:
     *
     *     1. [system identifier] The system identifier of the external subset, if
     *        it exists. Otherwise this property has no value.
     *
     * @return the system identifier String object, or null if there is none.
     */
    public String getDocumentTypeDeclarationSystemIdentifier() {
        return null;
    }

    /**
     * Return the public identifier of the external subset,
     * normalized as described in 4.2.2 External Entities [XML]. If there is
     * no external subset or if it has no public identifier, this property
     * has no value.
     *
     * @return the public identifier String object, or null if there is none.
     */
    public String getDocumentTypeDeclarationPublicIdentifier() {
        return null;
    }

    /**
     * Returns the <code>Element</code> whose <code>ID</code> is given by
     * <code>elementId</code>. If no such element exists, returns
     * <code>DTM.NULL</code>. Behavior is not defined if more than one element
     * has this <code>ID</code>. Attributes (including those
     * with the name "ID") are not of type ID unless so defined by DTD/Schema
     * information available to the DTM implementation.
     * Implementations that do not know whether attributes are of type ID or
     * not are expected to return <code>DTM.NULL</code>.
     *
     * <p>%REVIEW% Presumably IDs are still scoped to a single document,
     * and this operation searches only within a single document, right?
     * Wouldn't want collisions between DTMs in the same process.</p>
     *
     * @param elementId The unique <code>id</code> value for an element.
     * @return The handle of the matching element.
     */
    public int getElementById(String elementId) {
        return 0;
    }

    /**
     * The getUnparsedEntityURI function returns the URI of the unparsed
     * entity with the specified name in the same document as the context
     * node (see [3.3 Unparsed Entities]). It returns the empty string if
     * there is no such entity.
     * <p>
     * XML processors may choose to use the System Identifier (if one
     * is provided) to resolve the entity, rather than the URI in the
     * Public Identifier. The details are dependent on the processor, and
     * we would have to support some form of plug-in resolver to handle
     * this properly. Currently, we simply return the System Identifier if
     * present, and hope that it a usable URI or that our caller can
     * map it to one.
     * TODO: Resolve Public Identifiers... or consider changing function name.
     * <p>
     * If we find a relative URI
     * reference, XML expects it to be resolved in terms of the base URI
     * of the document. The DOM doesn't do that for us, and it isn't
     * entirely clear whether that should be done here; currently that's
     * pushed up to a higher level of our application. (Note that DOM Level
     * 1 didn't store the document's base URI.)
     * TODO: Consider resolving Relative URIs.
     * <p>
     * (The DOM's statement that "An XML processor may choose to
     * completely expand entities before the structure model is passed
     * to the DOM" refers only to parsed entities, not unparsed, and hence
     * doesn't affect this function.)
     *
     * @param name A string containing the Entity Name of the unparsed
     * entity.
     *
     * @return String containing the URI of the Unparsed Entity, or an
     * empty string if no such entity exists.
     */
    public String getUnparsedEntityURI(String name) {
        return null;
    }

    // ============== Boolean methods ================

    /**
     * Return true if the xsl:strip-space or xsl:preserve-space was processed
     * during construction of the DTM document.
     *
     * <p>%REVEIW% Presumes a 1:1 mapping from DTM to Document, since
     * we aren't saying which Document to query...?</p>
     */
    public boolean supportsPreStripping() {
        return false;
    }

    /**
     * Figure out whether nodeHandle2 should be considered as being later
     * in the document than nodeHandle1, in Document Order as defined
     * by the XPath model. This may not agree with the ordering defined
     * by other XML applications.
     * <p>
     * There are some cases where ordering isn't defined, and neither are
     * the results of this function -- though we'll generally return true.
     *
     * TODO: Make sure this does the right thing with attribute nodes!!!
     *
     * @param nodeHandle1 DOM Node to perform position comparison on.
     * @param nodeHandle2 DOM Node to perform position comparison on .
     *
     * @return false if node2 comes before node1, otherwise return true.
     * You can think of this as
     * <code>(node1.documentOrderPosition &lt;= node2.documentOrderPosition)</code>.
     */
    public boolean isNodeAfter(int nodeHandle1, int nodeHandle2) {
        return false;
    }

    /**
     *     2. [element content whitespace] A boolean indicating whether the
     *        character is white space appearing within element content (see [XML],
     *        2.10 "White Space Handling"). Note that validating XML processors are
     *        required by XML 1.0 to provide this information. If there is no
     *        declaration for the containing element, this property has no value for
     *        white space characters. If no declaration has been read, but the [all
     *        declarations processed] property of the document information item is
     *        false (so there may be an unread declaration), then the value of this
     *        property is unknown for white space characters. It is always false for
     *        characters that are not white space.
     *
     * @param nodeHandle the node ID.
     * @return <code>true</code> if the character data is whitespace;
     *         <code>false</code> otherwise.
     */
    public boolean isCharacterElementContentWhitespace(int nodeHandle) {
        return false;
    }

    /**
     *    10. [all declarations processed] This property is not strictly speaking
     *        part of the infoset of the document. Rather it is an indication of
     *        whether the processor has read the complete DTD. Its value is a
     *        boolean. If it is false, then certain properties (indicated in their
     *        descriptions below) may be unknown. If it is true, those properties
     *        are never unknown.
     *
     * @param documentHandle A node handle that must identify a document.
     * @return <code>true</code> if all declarations were processed;
     *         <code>false</code> otherwise.
     */
    public boolean isDocumentAllDeclarationsProcessed(int documentHandle) {
        return false;
    }

    /**
     *     5. [specified] A flag indicating whether this attribute was actually
     *        specified in the start-tag of its element, or was defaulted from the
     *        DTD.
     *
     * @param attributeHandle the attribute handle
     * @return <code>true</code> if the attribute was specified;
     *         <code>false</code> if it was defaulted.
     */
    public boolean isAttributeSpecified(int attributeHandle) {
        return false;
    }

    // ========== Direct SAX Dispatch, for optimization purposes ========

    /**
     * Directly call the
     * characters method on the passed ContentHandler for the
     * string-value of the given node (see http://www.w3.org/TR/xpath#data-model
     * for the definition of a node's string-value). Multiple calls to the
     * ContentHandler's characters methods may well occur for a single call to
     * this method.
     *
     * @param nodeHandle The node ID.
     * @param ch A non-null reference to a ContentHandler.
     *
     * @throws org.xml.sax.SAXException
     */
    public void dispatchCharactersEvents(int nodeHandle, org.xml.sax.ContentHandler ch, boolean normalize) throws org.xml.sax.SAXException {
    }

    /**
     * Directly create SAX parser events from a subtree.
     *
     * @param nodeHandle The node ID.
     * @param ch A non-null reference to a ContentHandler.
     *
     * @throws org.xml.sax.SAXException
     */

    public void dispatchToEvents(int nodeHandle, org.xml.sax.ContentHandler ch) throws org.xml.sax.SAXException {
    }

    /**
     * Return an DOM node for the given node.
     *
     * @param nodeHandle The node ID.
     *
     * @return A node representation of the DTM node.
     */
    public org.w3c.dom.Node getNode(int nodeHandle) {
        return null;
    }

    // ==== Construction methods (may not be supported by some implementations!) =====
    // %REVIEW% jjk: These probably aren't the right API. At the very least
    // they need to deal with current-insertion-location and end-element
    // issues.

    /**
     * Append a child to the end of the child list of the current node. Please note that the node
     * is always cloned if it is owned by another document.
     *
     * <p>%REVIEW% "End of the document" needs to be defined more clearly.
     * Does it become the last child of the Document? Of the root element?</p>
     *
     * @param newChild Must be a valid new node handle.
     * @param clone true if the child should be cloned into the document.
     * @param cloneDepth if the clone argument is true, specifies that the
     *                   clone should include all it's children.
     */
    public void appendChild(int newChild, boolean clone, boolean cloneDepth) {
        boolean sameDoc = ((newChild & DOCHANDLE_MASK) == m_docHandle);
        if (clone || !sameDoc) {

        } else {

        }
    }

    /**
     * Append a text node child that will be constructed from a string,
     * to the end of the document.
     *
     * <p>%REVIEW% "End of the document" needs to be defined more clearly.
     * Does it become the last child of the Document? Of the root element?</p>
     *
     * @param str Non-null reference to a string.
     */
    public void appendTextChild(String str) {
        // ###shs Think more about how this differs from createTextNode
        //%TBD%
    }

    //================================================================
    // ==== BUILDER methods ====
    // %TBD% jjk: SHOULD PROBABLY BE INLINED, unless we want to support
    // both SAX1 and SAX2 and share this logic between them.

    /** Append a text child at the current insertion point. Assumes that the
     * actual content of the text has previously been appended to the m_char
     * buffer (shared with the builder).
     *
     * @param m_char_current_start int Starting offset of node's content in m_char.
     * @param contentLength int Length of node's content in m_char.
     * */
    void appendTextChild(int m_char_current_start, int contentLength) {
        // create a Text Node
        // %TBD% may be possible to combine with appendNode()to replace the next chunk of code
        int w0 = TEXT_NODE;
        // W1: Parent
        int w1 = currentParent;
        // W2: Start position within m_char
        int w2 = m_char_current_start;
        // W3: Length of the full string
        int w3 = contentLength;

        int ourslot = appendNode(w0, w1, w2, w3);
        previousSibling = ourslot;
    }

    /** Append a comment child at the current insertion point. Assumes that the
     * actual content of the comment has previously been appended to the m_char
     * buffer (shared with the builder).
     *
     * @param m_char_current_start int Starting offset of node's content in m_char.
     * @param contentLength int Length of node's content in m_char.
     * */
    void appendComment(int m_char_current_start, int contentLength) {
        // create a Comment Node
        // %TBD% may be possible to combine with appendNode()to replace the next chunk of code
        int w0 = COMMENT_NODE;
        // W1: Parent
        int w1 = currentParent;
        // W2: Start position within m_char
        int w2 = m_char_current_start;
        // W3: Length of the full string
        int w3 = contentLength;

        int ourslot = appendNode(w0, w1, w2, w3);
        previousSibling = ourslot;
    }

    /** Append an Element child at the current insertion point. This
     * Element then _becomes_ the insertion point; subsequent appends
     * become its lastChild until an appendEndElement() call is made.
     *
     * Assumes that the symbols (local name, namespace URI and prefix)
     * have already been added to the pools
     *
     * Note that this _only_ handles the Element node itself. Attrs and
     * namespace nodes are unbundled in the ContentHandler layer
     * and appended separately.
     *
     * @param namespaceIndex: Index within the namespaceURI string pool
     * @param localNameIndex Index within the local name string pool
     * @param prefixIndex: Index within the prefix string pool
     * */
    void appendStartElement(int namespaceIndex, int localNameIndex, int prefixIndex) {
        // do document root node creation here on the first element, create nodes for
        // this element and its attributes, store the element, namespace, and attritute
        // name indexes to the nodes array, keep track of the current node and parent
        // element used

        // W0  High:  Namespace  Low:  Node Type
        int w0 = (namespaceIndex << 16) | ELEMENT_NODE;
        // W1: Parent
        int w1 = currentParent;
        // W2: Next  (initialized as 0)
        int w2 = 0;
        // W3: Tagname high: prefix Low: local name
        int w3 = localNameIndex | prefixIndex << 16;
        /**/System.out.println("set w3=" + w3 + " " + (w3 >> 16) + "/" + (w3 & 0xffff));

        //int ourslot = nodes.appendSlot(w0, w1, w2, w3);
        int ourslot = appendNode(w0, w1, w2, w3);
        currentParent = ourslot;
        previousSibling = 0;

        // set the root element pointer when creating the first element node
        if (m_docElement == NULL)
            m_docElement = ourslot;
    }

    /** Append a Namespace Declaration child at the current insertion point.
     * Assumes that the symbols (namespace URI and prefix) have already been
     * added to the pools
     *
     * @param prefixIndex: Index within the prefix string pool
     * @param namespaceIndex: Index within the namespaceURI string pool
     * @param isID: If someone really insists on writing a bad DTD, it is
     * theoretically possible for a namespace declaration to also be declared
     * as being a node ID. I don't really want to support that stupidity,
     * but I'm not sure we can refuse to accept it.
     * */
    void appendNSDeclaration(int prefixIndex, int namespaceIndex, boolean isID) {
        // %REVIEW% I'm assigning this node the "namespace for namespaces"
        // which the DOM defined. It is expected that the Namespace spec will
        // adopt this as official. It isn't strictly needed since it's implied
        // by the nodetype, but for now...

        // %REVIEW% Prefix need not be recorded; it's implied too. But
        // recording it might simplify the design.

        // %TBD% isID is not currently honored.

        final int namespaceForNamespaces = m_nsNames.stringToIndex("http://www.w3.org/2000/xmlns/");

        // W0  High:  Namespace  Low:  Node Type
        int w0 = NAMESPACE_NODE | (m_nsNames.stringToIndex("http://www.w3.org/2000/xmlns/") << 16);

        // W1:  Parent
        int w1 = currentParent;
        // W2:  CURRENTLY UNUSED -- It's next-sib in attrs, but we have no kids.
        int w2 = 0;
        // W3:  namespace name
        int w3 = namespaceIndex;
        // Add node
        int ourslot = appendNode(w0, w1, w2, w3);
        previousSibling = ourslot; // Should attributes be previous siblings
        previousSiblingWasParent = false;
        return;//(m_docHandle | ourslot);
    }

    /** Append an Attribute child at the current insertion
     * point.  Assumes that the symbols (namespace URI, local name, and
     * prefix) have already been added to the pools, and that the content has
     * already been appended to m_char. Note that the attribute's content has
     * been flattened into a single string; DTM does _NOT_ attempt to model
     * the details of entity references within attribute values.
     *
     * @param namespaceIndex int Index within the namespaceURI string pool
     * @param localNameIndex int Index within the local name string pool
     * @param prefixIndex int Index within the prefix string pool
     * @param isID boolean True if this attribute was declared as an ID
     * (for use in supporting getElementByID).
     * @param m_char_current_start int Starting offset of node's content in m_char.
     * @param contentLength int Length of node's content in m_char.
     * */
    void appendAttribute(int namespaceIndex, int localNameIndex, int prefixIndex, boolean isID, int m_char_current_start, int contentLength) {
        // %TBD% isID is not currently honored.

        // W0  High:  Namespace  Low:  Node Type
        int w0 = ATTRIBUTE_NODE | namespaceIndex << 16;

        // W1:  Parent
        int w1 = currentParent;
        // W2:  Next (not yet resolved)
        int w2 = 0;
        // W3:  Tagname high: prefix Low: local name
        int w3 = localNameIndex | prefixIndex << 16;
        /**/System.out.println("set w3=" + w3 + " " + (w3 >> 16) + "/" + (w3 & 0xffff));
        // Add node
        int ourslot = appendNode(w0, w1, w2, w3);
        previousSibling = ourslot; // Should attributes be previous siblings

        // Attribute's content is currently appended as a Text Node

        // W0: Node Type
        w0 = TEXT_NODE;
        // W1: Parent
        w1 = ourslot;
        // W2: Start Position within buffer
        w2 = m_char_current_start;
        // W3: Length
        w3 = contentLength;
        appendNode(w0, w1, w2, w3);

        // Attrs are Parents
        previousSiblingWasParent = true;
        return;//(m_docHandle | ourslot);
    }

    /**
     * This returns a stateless "traverser", that can navigate over an
     * XPath axis, though not in document order.
     *
     * @param axis One of Axes.ANCESTORORSELF, etc.
     *
     * @return A DTMAxisIterator, or null if the given axis isn't supported.
     */
    public DTMAxisTraverser getAxisTraverser(final int axis) {
        return null;
    }

    /**
     * This is a shortcut to the iterators that implement the
     * supported XPath axes (only namespace::) is not supported.
     * Returns a bare-bones iterator that must be initialized
     * with a start node (using iterator.setStartNode()).
     *
     * @param axis One of Axes.ANCESTORORSELF, etc.
     *
     * @return A DTMAxisIterator, or null if the given axis isn't supported.
     */
    public DTMAxisIterator getAxisIterator(final int axis) {
        // %TBD%
        return null;
    }

    /**
     * Get an iterator that can navigate over an XPath Axis, predicated by
     * the extended type ID.
     *
     *
     * @param axis
     * @param type An extended type ID.
     *
     * @return A DTMAxisIterator, or null if the given axis isn't supported.
     */
    public DTMAxisIterator getTypedAxisIterator(final int axis, final int type) {
        // %TBD%
        return null;
    }

    /** Terminate the element currently acting as an insertion point. Subsequent
     * insertions will occur as the last child of this element's parent.
     * */
    void appendEndElement() {
        // pop up the stacks

        if (previousSiblingWasParent)
            nodes.writeEntry(previousSibling, 2, NULL);

        // Pop parentage
        previousSibling = currentParent;
        nodes.readSlot(currentParent, gotslot);
        currentParent = gotslot[1] & 0xFFFF;

        // The element just being finished will be
        // the previous sibling for the next operation
        previousSiblingWasParent = true;

        // Pop a level of namespace table
        // namespaceTable.removeLastElem();
    }

    /**  Starting a new document. Perform any resets/initialization
     * not already handled.
     * */
    void appendStartDocument() {

        // %TBD% reset slot 0 to indicate ChunkedIntArray reuse or wait for
        //       the next initDocument().
        m_docElement = NULL; // reset nodeHandle to the root of the actual dtm doc content
        initDocument(0);
    }

    /**  All appends to this document have finished; do whatever final
     * cleanup is needed.
     * */
    void appendEndDocument() {
        done = true;
        // %TBD% may need to notice the last slot number and slot count to avoid
        // residual data from provious use of this DTM
    }

    /**
     * For the moment all the run time properties are ignored by this
     * class.
     *
     * @param property a <code>String</code> value
     * @param value an <code>Object</code> value
     */
    public void setProperty(String property, Object value) {
    }

    /**
     * Source information is not handled yet, so return
     * <code>null</code> here.
     *
     * @param node an <code>int</code> value
     * @return null
     */
    public SourceLocator getSourceLocatorFor(int node) {
        return null;
    }

    /**
     * A dummy routine to satisify the abstract interface. If the DTM
     * implememtation that extends the default base requires notification
     * of registration, they can override this method.
     */
    public void documentRegistration() {
    }

    /**
     * A dummy routine to satisify the abstract interface. If the DTM
     * implememtation that extends the default base requires notification
     * when the document is being released, they can override this method
     */
    public void documentRelease() {
    }

    /**
     * Migrate a DTM built with an old DTMManager to a new DTMManager.
     * After the migration, the new DTMManager will treat the DTM as
     * one that is built by itself.
     * This is used to support DTM sharing between multiple transformations.
     * @param manager the DTMManager
     */
    public void migrateTo(DTMManager manager) {
    }

}
